 /*******************************************************************************
  * Copyright (c) 2003, 2007 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
  * IBM - Initial API and implementation
  *******************************************************************************/
 package org.eclipse.ui.internal.progress;

 import java.lang.reflect.InvocationTargetException ;
 import java.util.Collection ;
 import java.util.Collections ;
 import java.util.HashSet ;

 import org.eclipse.core.runtime.Assert;
 import org.eclipse.core.runtime.IProgressMonitor;
 import org.eclipse.core.runtime.IStatus;
 import org.eclipse.core.runtime.Status;
 import org.eclipse.core.runtime.jobs.IJobChangeEvent;
 import org.eclipse.core.runtime.jobs.IJobChangeListener;
 import org.eclipse.core.runtime.jobs.ISchedulingRule;
 import org.eclipse.core.runtime.jobs.Job;
 import org.eclipse.core.runtime.jobs.JobChangeAdapter;
 import org.eclipse.jface.operation.IRunnableContext;
 import org.eclipse.jface.operation.IRunnableWithProgress;
 import org.eclipse.jface.resource.ImageDescriptor;
 import org.eclipse.jface.util.IPropertyChangeListener;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.graphics.Cursor;
 import org.eclipse.swt.graphics.Image;
 import org.eclipse.swt.widgets.Control;
 import org.eclipse.swt.widgets.Display;
 import org.eclipse.swt.widgets.Shell;
 import org.eclipse.ui.IWorkbenchPart;
 import org.eclipse.ui.PlatformUI;
 import org.eclipse.ui.internal.PartSite;
 import org.eclipse.ui.internal.WorkbenchPlugin;
 import org.eclipse.ui.part.WorkbenchPart;
 import org.eclipse.ui.progress.IProgressService;
 import org.eclipse.ui.progress.IWorkbenchSiteProgressService;
 import org.eclipse.ui.progress.WorkbenchJob;

 /**
  * The WorkbenchSiteProgressService is the concrete implementation of the
  * WorkbenchSiteProgressService used by the workbench components.
  */
 public class WorkbenchSiteProgressService implements
         IWorkbenchSiteProgressService, IJobBusyListener {
     PartSite site;

     private Collection busyJobs = Collections.synchronizedSet(new HashSet ());

     private Object busyLock = new Object ();

     IJobChangeListener listener;

     IPropertyChangeListener[] changeListeners = new IPropertyChangeListener[0];

     private Cursor waitCursor;

     private SiteUpdateJob updateJob;

     /**
      * Flag that keeps state from calls to {@link #showBusy(boolean)}
      */
     private int busyCount = 0;

     private class SiteUpdateJob extends WorkbenchJob {
         private boolean busy;

         private boolean useWaitCursor = false;

         Object lock = new Object ();

         /**
          * Set whether we are updating with the wait or busy cursor.
          *
          * @param cursorState
          */
         void setBusy(boolean cursorState) {
             synchronized (lock) {
                 busy = cursorState;
             }
         }

         private SiteUpdateJob() {
             super(ProgressMessages.WorkbenchSiteProgressService_CursorJob);
         }

         /**
          * Get the wait cursor. Initialize it if required.
          * @param display the display to create the cursor on.
          * @return the created cursor
          */
         private Cursor getWaitCursor(Display display) {
             if (waitCursor == null) {
                 waitCursor = new Cursor(display, SWT.CURSOR_APPSTARTING);
             }
             return waitCursor;
         }

         /*
          * (non-Javadoc)
          *
          * @see org.eclipse.ui.progress.UIJob#runInUIThread(org.eclipse.core.runtime.IProgressMonitor)
          */
         public IStatus runInUIThread(IProgressMonitor monitor) {
             Control control = site.getPane().getControl();
             if (control == null || control.isDisposed()) {
                 return Status.CANCEL_STATUS;
             }
             synchronized (lock) {
                 //Update cursors if we are doing that
 if (useWaitCursor) {
                     Cursor cursor = null;
                     if (busy) {
                         cursor = getWaitCursor(control.getDisplay());
                     }
                     control.setCursor(cursor);
                 }
                 site.getPane().setBusy(busy);
                 IWorkbenchPart part = site.getPart();
                  if (part instanceof WorkbenchPart) {
                     ((WorkbenchPart) part).showBusy(busy);
                 }
             }
             return Status.OK_STATUS;
         }

         void clearCursors() {
             if (waitCursor != null) {
                 waitCursor.dispose();
                 waitCursor = null;
             }
         }
     }

     /**
      * Create a new instance of the receiver with a site of partSite
      *
      * @param partSite
      * PartSite.
      */
     public WorkbenchSiteProgressService(final PartSite partSite) {
         site = partSite;
         updateJob = new SiteUpdateJob();
         updateJob.setSystem(true);
     }

     /**
      * Dispose the resources allocated by the receiver.
      *
      */
     public void dispose() {
         if (updateJob != null) {
             updateJob.cancel();
         }

         ProgressManager.getInstance().removeListener(this);

         if (waitCursor == null) {
             return;
         }
         waitCursor.dispose();
         waitCursor = null;
     }

     /*
      * (non-Javadoc)
      *
      * @see org.eclipse.ui.progress.IProgressService#busyCursorWhile(org.eclipse.jface.operation.IRunnableWithProgress)
      */
     public void busyCursorWhile(IRunnableWithProgress runnable)
             throws InvocationTargetException , InterruptedException {
         getWorkbenchProgressService().busyCursorWhile(runnable);
     }

     /*
      * (non-Javadoc)
      *
      * @see org.eclipse.ui.progress.IWorkbenchSiteProgressService#schedule(org.eclipse.core.runtime.jobs.Job,
      * long, boolean)
      */
     public void schedule(Job job, long delay, boolean useHalfBusyCursor) {
         job.addJobChangeListener(getJobChangeListener(job, useHalfBusyCursor));
         job.schedule(delay);
     }

     /*
      * (non-Javadoc)
      *
      * @see org.eclipse.ui.progress.IWorkbenchSiteProgressService#schedule(org.eclipse.core.runtime.jobs.Job,
      * int)
      */
     public void schedule(Job job, long delay) {
         schedule(job, delay, false);
     }

     /*
      * (non-Javadoc)
      *
      * @see org.eclipse.ui.progress.IWorkbenchSiteProgressService#schedule(org.eclipse.core.runtime.jobs.Job)
      */
     public void schedule(Job job) {
         schedule(job, 0L, false);
     }

     /*
      * (non-Javadoc)
      *
      * @see org.eclipse.ui.progress.IWorkbenchSiteProgressService#showBusyForFamily(java.lang.Object)
      */
     public void showBusyForFamily(Object family) {
         ProgressManager.getInstance().addListenerToFamily(family, this);
     }

     /**
      * Get the job change listener for this site.
      *
      * @param job
      * @param useHalfBusyCursor
      * @return IJobChangeListener
      */
     public IJobChangeListener getJobChangeListener(final Job job,
             boolean useHalfBusyCursor) {
         if (listener == null) {
             updateJob.useWaitCursor = useHalfBusyCursor;
             listener = new JobChangeAdapter() {
                 /*
                  * (non-Javadoc)
                  *
                  * @see org.eclipse.core.runtime.jobs.JobChangeAdapter#aboutToRun(org.eclipse.core.runtime.jobs.IJobChangeEvent)
                  */
                 public void aboutToRun(IJobChangeEvent event) {
                     incrementBusy(event.getJob());
                 }

                 /*
                  * (non-Javadoc)
                  *
                  * @see org.eclipse.core.runtime.jobs.JobChangeAdapter#done(org.eclipse.core.runtime.jobs.IJobChangeEvent)
                  */
                 public void done(IJobChangeEvent event) {
                     decrementBusy(event.getJob());
                 }
             };
         }
         return listener;
     }

     /*
      * (non-Javadoc)
      *
      * @see org.eclipse.ui.internal.progress.IJobBusyListener#decrementBusy(org.eclipse.core.runtime.jobs.Job)
      */
     public void decrementBusy(Job job) {
         synchronized (busyLock) {
             if (!busyJobs.contains(job)) {
                 return;
             }
             busyJobs.remove(job);
         }
         try {
             decrementBusy();
         } catch (Exception ex) {
             // protecting against assertion failures
 WorkbenchPlugin.log(ex);
         }
     }

     /*
      * (non-Javadoc)
      *
      * @see org.eclipse.ui.internal.progress.IJobBusyListener#incrementBusy(org.eclipse.core.runtime.jobs.Job)
      */
     public void incrementBusy(Job job) {
         synchronized (busyLock) {
             if (busyJobs.contains(job)) {
                 return;
             }
             busyJobs.add(job);
         }
         incrementBusy();
     }

     /*
      * (non-Javadoc)
      *
      * @see org.eclipse.ui.progress.IWorkbenchSiteProgressService#warnOfContentChange()
      */
     public void warnOfContentChange() {
         site.getPane().showHighlight();
     }

     /*
      * (non-Javadoc)
      *
      * @see org.eclipse.ui.progress.IProgressService#showInDialog(org.eclipse.swt.widgets.Shell,
      * org.eclipse.core.runtime.jobs.Job)
      */
     public void showInDialog(Shell shell, Job job) {
         getWorkbenchProgressService().showInDialog(shell, job);
     }

     /**
      * Get the progress service for the workbnech,
      *
      * @return IProgressService
      */
     private IProgressService getWorkbenchProgressService() {
         return site.getWorkbenchWindow().getWorkbench().getProgressService();
     }

     /*
      * (non-Javadoc)
      *
      * @see org.eclipse.jface.operation.IRunnableContext#run(boolean, boolean,
      * org.eclipse.jface.operation.IRunnableWithProgress)
      */
     public void run(boolean fork, boolean cancelable,
             IRunnableWithProgress runnable) throws InvocationTargetException ,
             InterruptedException {
         getWorkbenchProgressService().run(fork, cancelable, runnable);
     }

     /*
      * (non-Javadoc)
      * @see org.eclipse.ui.progress.IProgressService#runInUI(org.eclipse.jface.operation.IRunnableContext, org.eclipse.jface.operation.IRunnableWithProgress, org.eclipse.core.runtime.jobs.ISchedulingRule)
      */
     public void runInUI(IRunnableContext context,
             IRunnableWithProgress runnable, ISchedulingRule rule)
             throws InvocationTargetException , InterruptedException {
         getWorkbenchProgressService().runInUI(context, runnable, rule);
     }

     /* (non-Javadoc)
      * @see org.eclipse.ui.progress.IProgressService#getLongOperationTime()
      */
     public int getLongOperationTime() {
         return getWorkbenchProgressService().getLongOperationTime();
     }

     /* (non-Javadoc)
      * @see org.eclipse.ui.progress.IProgressService#registerIconForFamily(org.eclipse.jface.resource.ImageDescriptor, java.lang.Object)
      */
     public void registerIconForFamily(ImageDescriptor icon, Object family) {
         getWorkbenchProgressService().registerIconForFamily(icon, family);
     }

     /* (non-Javadoc)
      * @see org.eclipse.ui.progress.IProgressService#getIconFor(org.eclipse.core.runtime.jobs.Job)
      */
     public Image getIconFor(Job job) {
         return getWorkbenchProgressService().getIconFor(job);
     }

     /* (non-Javadoc)
      * @see org.eclipse.ui.progress.IWorkbenchSiteProgressService#showBusy(boolean)
      */
     public void incrementBusy() {
         synchronized (busyLock) {
             this.busyCount++;
             if (busyCount != 1) {
                 return;
             }
             updateJob.setBusy(true);
         }
         if (PlatformUI.isWorkbenchRunning()) {
             updateJob.schedule(100);
         } else {
             updateJob.cancel();
         }
     }
     /* (non-Javadoc)
      * @see org.eclipse.ui.progress.IWorkbenchSiteProgressService#showBusy(boolean)
      */
     public void decrementBusy() {
         synchronized (busyLock) {
             Assert
                     .isTrue(
                             busyCount > 0,
                             "Ignoring unexpected call to IWorkbenchSiteProgressService.decrementBusy(). This might be due to an earlier call to this method."); //$NON-NLS-1$
 this.busyCount--;
             if (busyCount != 0) {
                 return;
             }
             updateJob.setBusy(false);
         }
         if (PlatformUI.isWorkbenchRunning()) {
             updateJob.schedule(100);
         } else {
             updateJob.cancel();
         }
     }
 }

